#include "../include/singleton.h"

void CarAllocationSystem::test(int index, int order){
    cars[index].test(order);
}


/*
    下面是一些自定义的函数，帮助实现各个方法
*/
// 生成一个自定义的cqusn打头的16位数字+字母的小车编号
std::string generateCarID(int num)
{
    // 简单起见，直接前面部分自定义，后面跟着一个序号
    // 前14位
    std::string result = "cqusnE20200340";
    // 后2位
    if (num < 10)
    {
        result += "0";
    }
    result += std::to_string(num);
    return result;
}

// 移到外面（在函数体里面，则剩余的随机字符串相同）
std::mt19937 gen((unsigned int)time(NULL));
// 生成底盘的ID。也尝试一下运用随机数生成不同的ID
std::string generateDpID(int num)
{
    const std::string characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const int length = 8;
    // 关于random的一些用法，参考了 https://blog.csdn.net/Alfa_/article/details/125215799
    std::random_device rd; // 随机数发生器
    // std::mt19937 gen(rd()); // 随机数引擎
    std::uniform_int_distribution<int> distribution(0, characters.length() - 1);
    std::string result = "dp";
    // 将输入的num转换为字符串，并拼接到结果中，确保id肯定不一样
    if (num < 10)
    {
        result += "0";
    }
    result += std::to_string(num);
    // 生成剩余的随机字符
    for (int i = 0; i < length - 4; i++)
    {
        result += characters[distribution(gen)];
    }
    return result;
}

// 生成学生ID
std::string generateStuID(int num)
{
    // 前几位
    std::string result = "E20202";
    // 后2位
    if (num < 10)
    {
        result += "0";
    }
    result += std::to_string(num);
    return result;
}

// 生成学生姓名
std::mt19937 gen2((unsigned int)time(NULL));
std::string generateStuName(int num)
{
    // 汉字，那就用char二维数组记录。姓氏
    const char surname[24][10] = {"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "楮", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张"};
    // 名
    const char firstName[24][10] = {"浩然", "思琪", "雨轩", "若云", "曦", "傲天", "强", "梦", "如奕", "明轩", "天翊", "成道", "景铄", "一铭", "龙", "旭乐", "志南", "韵", "昕", "玥", "菡", "莹然", "夕", "溪瑶"};
    std::random_device rd; // 随机数发生器
    std::uniform_int_distribution<int> distribution1(0, 23);
    std::uniform_int_distribution<int> distribution2(0, 23);
    std::string result = surname[distribution1(gen2)];
    result += firstName[distribution2(gen2)];
    return result;
}

/*
    下面是成员函数的实现
*/
// 1、小车初始化函数
void CarAllocationSystem::car_init()
{
    std::cout << "开始初始化小车信息" << std::endl;
    Car carDemo;
    // 一共有count辆小车，依次push进去。
    for (int i = 0; i < count; i++)
    {
        carDemo.setID(generateCarID(i + 1));
        carDemo.setBasePlateID(generateDpID(i + 1));

        cars.push_back(carDemo);
    }
    std::cout << "完成小车信息初始化" << std::endl;
}

// 2、学生初始化函数
void CarAllocationSystem::stu_init()
{
    std::cout << "开始初始化学生信息" << std::endl;
    for (int i = 0; i < count; i++)
    {
        Student stu = Student(generateStuID(i + 1), generateStuName(i + 1));
        stu.print();
        students.push_back(stu);
    }
    std::cout << "完成学生信息初始化" << std::endl;
}

// 3、分配
void CarAllocationSystem::allocate()
{
    std::cout << "开始分配" << std::endl;
    // map[小车下标] = 学生下标
    std::random_device rd;
    std::mt19937 gen(rd());
    // 一个长度为count的vector，
    std::vector<int> b(count);
    for (int i = 0; i < count; i++)
    {
        b[i] = i;
    }
    // 洗牌
    std::shuffle(b.begin(), b.end(), gen);
    // 随机匹配的结果。让0到10下标的小车，依次对应一个随机的学生。
    for (int i = 0; i < count; i++)
    {
        stuToCar[i] = b[i];
    }
    std::cout << "完成分配" << std::endl;
}

// 4、保存到本地文件
void CarAllocationSystem::write()
{
    std::cout << "开始保存\n";
    // 不加ios::app，直接覆盖掉上一次运行的
    std::ofstream file(fileName);
    if (!file.is_open())
    {
        std::cout << "未找到文件！" << std::endl;
        return;
    }

    // 提前写一行标题行，便于阅读
    file << "学号,姓名,智能小车编号,底盘驱动形式,底盘最小离地间隙(mm),底盘编号,最大行程(km),底盘型号,轮胎1型号,轮胎1尺寸(mm),轮胎2型号,轮胎2尺寸(mm),轮胎3型号,轮胎3尺寸(mm),轮胎4型号,轮胎4尺寸(mm),轮距(mm),最小转弯半径(m),轴距(mm),AI(TOPS),CUDA核心,显存(GB),AGX型号,存储(GB),Tensor CORE,摄像头,深度帧率,FOV-X,FOV-Y,双目摄像头型号,RGB帧率,RGB帧分辨率-X,RGB帧分辨率-Y,雷达通道数,雷达测试范围(m),多线激光雷达型号,雷达功耗(W),陀螺仪厂家,陀螺仪型号,显示屏型号,显示屏尺寸,电池充电时长(h),对外供电(V),参数" << std::endl;

    // 关闭文件流
    file.close();

    // 接下来，遍历小车，交替save学生、save小车信息。并且加上ios::app，从末尾开始写
    for (int i = 0; i < count; i++)
    {
        // i是学生下标，stuToCar[i]是小车下标
        int cId = stuToCar[i];

        // 先学生，后小车，交替save
        students[i].save(fileName);
        cars[cId].save(fileName);

        // 换行
        std::ofstream file(fileName, std::ios::app);
        if (!file.is_open())
        {
            std::cout << "未找到文件！" << std::endl;
            return;
        }
        file << std::endl;
        file.close();
    }
    std::cout << "完成保存\n";
}

// 5、信息读取与展示
void CarAllocationSystem::read()
{
    std::cout << "开始读取\n";
    std::ifstream file(fileName);
    if (!file.is_open())
    {
        std::cout << "未找到文件！" << std::endl;
        return;
    }

    // 进行数据读取
    int index = 0;
    std::string line;
    std::getline(file, line); // 去掉标题行
    Car car1;
    cars.resize(count, car1);
    Student stu;
    students.resize(count, stu);
    while (std::getline(file, line))
    {

        std::stringstream ss(line);
        std::string token;
        std::vector<std::string> tokens;
        while (std::getline(ss, token, ','))
        {
            tokens.push_back(token);
        }

        // 将tokens赋值给结构体。
        int i = 0;
        students[index].setStudentID(tokens[i++]);
        students[index].setName(tokens[i++]);
        // 直接设置id之后会被Car的构造函数覆盖掉，就无效了。可以记录下来，后面再set一次
        // cars[index].setID(tokens[i++]);
        std::string carId = tokens[i++];

        BasePlate basePlate;
        basePlate.setDriveType(tokens[i++]);
        basePlate.setGroundClearance(std::stoi(tokens[i++])); // 115
        basePlate.setId(tokens[i++]);
        basePlate.setMaxRange(std::stoi(tokens[i++])); // 10
        basePlate.setModel(tokens[i++]);

        basePlate.setTire(0, tokens[i], std::stoi(tokens[i + 1]));
        i += 2;
        basePlate.setTire(1, tokens[i], std::stoi(tokens[i + 1]));
        i += 2;
        basePlate.setTire(2, tokens[i], std::stoi(tokens[i + 1]));
        i += 2;
        basePlate.setTire(3, tokens[i], std::stoi(tokens[i + 1]));
        i += 2;

        basePlate.setTrack(std::stoi(tokens[i++]));         // 490
        basePlate.setTurningRadius(std::stoi(tokens[i++])); // 0
        basePlate.setWheelbase(std::stoi(tokens[i++]));     // 451

        AGX agx;
        agx.setAiPerformance(std::stoi(tokens[i++])); // 32
        agx.setCudaCores(std::stoi(tokens[i++]));     // 512
        agx.setMemory(std::stoi(tokens[i++]));        // 32
        agx.setModel(tokens[i++]);
        agx.setStorage(std::stoi(tokens[i++]));     // 32
        agx.setTensorCores(std::stoi(tokens[i++])); // 64

        BinocularCamera binocularCamera;
        binocularCamera.setCamera(tokens[i++]);
        binocularCamera.setDepthFrameRate(std::stoi(tokens[i++])); // 90
        binocularCamera.setFovX(std::stoi(tokens[i++]));           // 87
        binocularCamera.setFovY(std::stoi(tokens[i++]));           // 58
        binocularCamera.setModel(tokens[i++]);
        binocularCamera.setRgbFrameRate(std::stoi(tokens[i++]));   // 30
        binocularCamera.setRgbResolutionX(std::stoi(tokens[i++])); // 1920
        binocularCamera.setRgbResolutionY(std::stoi(tokens[i++])); // 1080

        Ladar ladar;
        ladar.setChannels(std::stoi(tokens[i++])); // 16
        ladar.setMaxRange(std::stoi(tokens[i++])); // 100
        ladar.setModel(tokens[i++]);
        ladar.setPowerConsumed(std::stoi(tokens[i++])); // 8

        Gyroscope gyroscope;
        gyroscope.setManufacturer(tokens[i++]);
        gyroscope.setModel(tokens[i++]);

        LCD lcd;
        lcd.setModel(tokens[i++]);
        lcd.setSize(std::stof(tokens[i++])); // 11.6 注意这里是stof！！！！

        Battery battery;
        battery.setChargingTime(std::stoi(tokens[i++]));  // 2
        battery.setOutputVoltage(std::stoi(tokens[i++])); // 24
        battery.setParameters(tokens[i++]);

        cars[index] = Car(basePlate, agx, binocularCamera, ladar, gyroscope, lcd, battery);
        cars[index].setID(carId);
        index++;
    }

    // 展示信息
    int lineCnt = 0;

    std::cout << "\n**********************************************\n";
    students[lineCnt].print();
    std::cout << "**********************************************";
    cars[lineCnt].print();
    std::cout << "**********************************************\n";
    std::cout << "请输入n显示下一条记录，p显示上一条记录，q退出:\n";

    while (true)
    {
        char choice;
        std::cin >> choice;
        // q退出
        if (choice == 'q')
        {
            break;
        }
        // n下一个
        else if (choice == 'n')
        {
            if (lineCnt != count - 1)
            {
                // showInfo(++lineCnt);
                lineCnt++;
                std::cout << "\n**********************************************\n";
                students[lineCnt].print();
                std::cout << "**********************************************";
                cars[lineCnt].print();
                std::cout << "**********************************************\n";
                std::cout << "请输入n显示下一条记录，p显示上一条记录，q退出:\n";
            }
            else
            {
                std::cout << "已经是最后一辆! 请重新输入：\n";
            }
        }
        // p上一个
        else if (choice == 'p')
        {
            if (lineCnt != 0)
            {
                // showInfo(--lineCnt);
                lineCnt--;
                std::cout << "\n**********************************************\n";
                students[lineCnt].print();
                std::cout << "**********************************************";
                cars[lineCnt].print();
                std::cout << "**********************************************\n";
                std::cout << "请输入n显示下一条记录，p显示上一条记录，q退出:\n";
            }
            else
            {
                std::cout << "已经是第一辆! 请重新输入：\n";
            }
        }
    }

    // 关闭文件流
    file.close();
    std::cout << "结束读取\n";
}